<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class SanitizeInput
{
    /**
     * 需要清理HTML标签的字段（允许保留HTML，但会清理危险内容）
     *
     * @var array
     */
    protected $htmlFields = [
        'remark',
        'description',
        'content',
    ];

    /**
     * 不需要清理的字段（已经通过严格验证）
     *
     * @var array
     */
    protected $excludedFields = [
        'username', // 已通过regex验证，只允许字母数字下划线
        'password', // 密码字段，由密码策略处理
        'password_confirmation',
        'old_password',
    ];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function handle(Request $request, Closure $next): Response
    {
        try {
            // 只对非JSON请求进行清理（JSON请求通常已经通过验证）
            // 或者根据Content-Type决定
            if ($request->isJson() || $request->expectsJson()) {
                // JSON请求通常已经验证，但仍需清理
                $input = $request->all();
                $cleaned = $this->cleanInput($input);
                $request->merge($cleaned);
            } else {
                // 表单请求需要清理
                $input = $request->all();
                $cleaned = $this->cleanInput($input);
                $request->merge($cleaned);
            }

            return $next($request);
        } catch (\Exception $e) {
            \Log::error('SanitizeInput中间件异常: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
            // 如果清理失败，继续处理请求（不阻塞）
            return $next($request);
        }
    }

    /**
     * 清理输入数据
     *
     * @param array $input
     * @return array
     */
    protected function cleanInput(array $input): array
    {
        foreach ($input as $key => $value) {
            // 跳过不需要清理的字段
            if (in_array($key, $this->excludedFields)) {
                continue;
            }
            
            if (is_array($value)) {
                $input[$key] = $this->cleanInput($value);
            } elseif (is_string($value)) {
                // 移除空字节
                $value = str_replace("\0", '', $value);
                
                // 移除控制字符（除了换行符和制表符）
                $value = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $value);
                
                // 对于HTML字段，清理HTML标签和脚本
                if (in_array($key, $this->htmlFields)) {
                    // 移除script标签
                    $value = preg_replace('/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi', '', $value);
                    // 移除on事件属性
                    $value = preg_replace('/\s*on\w+\s*=\s*["\'][^"\']*["\']/i', '', $value);
                } else {
                    // 对于非HTML字段，移除所有HTML标签（但保留基本文本）
                    $value = strip_tags($value);
                }
                
                // 注意：不要对所有字段都使用htmlspecialchars，这会影响JSON数据
                // XSS防护应该在前端和后端响应时处理，而不是在输入时
                // 这里只做基本的清理，详细的XSS防护在响应时通过SecurityHeaders处理
                
                // 限制字符串长度（防止DoS攻击）
                if (strlen($value) > 10000) {
                    $value = substr($value, 0, 10000);
                }
                
                $input[$key] = $value;
            }
        }

        return $input;
    }
}

